package com.allming.boot.baidu.map.service;

import com.allming.boot.baidu.map.autoconfigure.AppKeyProperties;
import com.allming.boot.baidu.map.model.AddressComponent;
import com.allming.boot.baidu.map.model.GeoCodingResp;
import com.allming.boot.baidu.map.model.Location;
import com.allming.boot.baidu.map.model.ReverseGeoCodingResp;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.outfit.boot.okhttp.OkHttpService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * 百度地图服务
 *
 * @author ming
 * @date 2020-06-30 11:23
 */
@Slf4j
public class BaiduMapService {

    @Resource
    private ObjectMapper objectMapper;

    private final OkHttpService okHttpService;
    private final AppKeyProperties appKeyProperties;

    public BaiduMapService(final AppKeyProperties appKeyProperties,
                           final OkHttpService okHttpService) {
        this.appKeyProperties = appKeyProperties;
        this.okHttpService = okHttpService;
    }

    /**
     * 地理编码
     * http://api.map.baidu.com/geocoding/v3/?address=北京市海淀区上地十街10号
     * GET请求
     *
     * @param address 待解析的地址。最多支持84个字节
     * @param city    地址所在的城市名。
     * @return GeoCodingResp
     */
    public GeoCodingResp geoCoding(String address, String city) throws IOException {
        Assert.isTrue(!StringUtils.isEmpty(address), "待解析的地址不能为空");
        address = address.trim();
        String url = "http://api.map.baidu.com/geocoding/v3/";
        Map<String, Object> params = new HashMap<>();
        address = URLEncoder.encode(address, StandardCharsets.UTF_8.name());
        params.put("address", address);
        params.put("city", city);
        params.put("ret_coordtype", "bd09ll");
        params.put("ak", appKeyProperties.getAppKey());
        params.put("sn", "");
        params.put("output", "json");
        params.put("callback", "");
        String s = okHttpService.get(url, params);
        if (StringUtils.isEmpty(s)) {
            log.error("geoCoding 百度返回数据为空。 params：{}", params);
            throw new IOException("geoCoding 百度返回数据为空");
        }
        try {
            GeoCodingResp resp = objectMapper.readValue(s, GeoCodingResp.class);
            if(resp == null || resp.getResult()==null ){
                log.error("geoCoding 百度返回数据格式异常 {}", s);
                throw new IllegalArgumentException("geoCoding 百度返回数据格式异常");
            }
            return resp;
        } catch (JsonProcessingException e) {
            log.error("geoCoding 百度返回数据格式异常 {}", s);
            throw new IllegalArgumentException("geoCoding 百度返回数据格式异常");
        }
    }

    /**
     * 获取经纬度坐标（解析异常时返回 null）
     * @param address 待解析的地址。最多支持84个字节
     * @param city    地址所在的城市名。
     * @return 经纬度坐标
     */
    public Location getLocation(String address, String city){
        try {
            return geoCoding(address, city).getResult().getLocation();
        } catch (IllegalArgumentException e) {
            throw e;
        }catch (Exception e) {
            log.error("getLocation Exception",e);
            return null;
        }
    }

    /**
     * 逆地理编码
     * http://api.map.baidu.com/reverse_geocoding/v3/?ak=您的ak&output=json&coordtype=wgs84ll&location=31.225696563611,121.49884033194
     * GET请求
     * @param location 经纬度坐标
     * @return ReverseGeoCodingResp
     */
    public ReverseGeoCodingResp reverseGeoCoding(Location location)throws IOException{
        Assert.notNull(location,"经纬度坐标不能为空");
        Assert.notNull(location.getLng(),"经度值不能为空");
        Assert.notNull(location.getLat(),"纬度值不能为空");
        String url = "http://api.map.baidu.com/reverse_geocoding/v3/";
        Map<String, Object> params = new HashMap<>();
        params.put("location", location.getLat()+","+location.getLng());
        params.put("coordtype", "bd09ll");
        params.put("ret_coordtype", "bd09ll");
        params.put("radius", 1000);
        params.put("ak", appKeyProperties.getAppKey());
        params.put("sn", "");
        params.put("output", "json");
        params.put("callback", "");
        params.put("language", "zh-CN");
        String s = okHttpService.get(url, params);
        if (StringUtils.isEmpty(s)) {
            log.error("reverseGeoCoding 百度返回数据为空。 params：{}", params);
            throw new IOException("reverseGeoCoding 百度返回数据为空");
        }
        try {
            return objectMapper.readValue(s, ReverseGeoCodingResp.class);
        } catch (JsonProcessingException e) {
            log.error("reverseGeoCoding 百度返回数据格式异常 {}", s);
            throw new IOException("reverseGeoCoding 百度返回数据格式异常");
        }
    }

    /**
     * 根据经纬度坐标获取对应地址信息（解析异常时返回 null）
     * @param location 经纬度坐标
     * @return 对应地址信息
     */
    public AddressComponent getAddressComponent(Location location){
        try {
            return reverseGeoCoding(location).getResult().getAddressComponent();
        } catch (IOException e) {
            return null;
        }
    }
}
